#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/wait.h>
#include <signal.h>

#include <calcLib.h>

#define MAXRECVSIZE 1500  //输入缓存数组的最大长度
#define BACKLOG 2   //允许多少个客户端同时连接服务端

int clientid = 0;          //当前客户端id
int listen_fd, client_fd;  //监听套接字，与客户端连接的套接字
struct sockaddr_in server;  //服务端地址结构体
struct sockaddr_in client;  //客户端地址结构体
socklen_t addrlen;          //地址长度
struct timeval timeout = {5, 0};

char msg[MAXRECVSIZE];    //作为接受客户端信息的存储数组，并多次重复使用
int readsize;
bool if_sleep = false;

void initsocket(const char *ip, int port) {
    if ((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {  //创建监听套接字
        perror("[Server] Info: socket");
        printf("[Server] Info: bind socket error\n");
        exit(1);
    }

    printf("[Server] Info: setting socket options\n");
    int opt = SO_REUSEADDR;
    if (setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1) {    //设置监听套接字选项
        printf("[Server] Error: set socket error\n");
        exit(1);
    }

    bzero(&server, sizeof(server));    //初始化服务端地址结构体
    server.sin_family = AF_INET;
    server.sin_port = htons(port);
    server.sin_addr.s_addr = inet_addr(ip);

    printf("[Server] Info: binding socket\n");
    if (bind(listen_fd, (struct sockaddr *) &server, sizeof(server)) == -1) {    //将监听套接字与本地地址绑定
        perror("[Server] Info: bind");
        printf("[Server] Info: bind socket error\n");
        exit(1);
    }

    printf("[Server] Info: listening to socekt\n");
    if (listen(listen_fd, BACKLOG) == -1) {     //启动监听套接字
        printf("[Server] Error :fail to listen to the socket\n");
        exit(1);
    }
    addrlen = sizeof(client);

    printf("[Server] Info: waiting for connection...\n");
}

void handleconnection() {
    while (1) {
        readsize = recv(client_fd, &msg, MAXRECVSIZE, 0);   //接受来自客户端的信息
        printf("[Server] Info: client send status \"%s\"\n", msg);

        msg[readsize] = '\0';   //为了在后续调用strcmp函数时与字符串常量成功进行比较，即模拟字符串常量以"\0"作为结尾
        if (strcmp(msg, "OK") != 0) {    //如果客户端不支持服务端的协议，则结束对话
            printf("[Server] Error: client does not support server\'s communication protocols\n");
            shutdown(client_fd, SHUT_RDWR);
            close(client_fd);
            break;
        }

        initCalcLib();    //在调用外教提供的指令生成包前必须调用他提供的初始化函数
        char operationCommand[MAXRECVSIZE];   //待返回客户端的计算题目字符串
        char *operation = randomType();   //随机计算题的字符串
        double fresult = 0;   //如果计算题是浮点计算，则将服务端计算结果存储在这里
        int result = 0;       //如果计算题是整数计算，则将服务端计算结果存储在这里
        if (operation[0] == 'f') {
            double value1 = randomFloat(), value2 = randomFloat();
            if (operation[1] == 'a') {
                fresult = value1 + value2;
            } else if (operation[1] == 's') {
                fresult = value1 - value2;
            } else if (operation[1] == 'm') {
                fresult = value1 * value2;
            } else {
                fresult = value1 / value2;
            }
            sprintf(operationCommand, "%s %f %f", operation, value1, value2);
        } else {
            int value1 = randomInt(), value2 = randomInt();
            if (operation[0] == 'a') {
                result = value1 + value2;
            } else if (operation[0] == 's') {
                result = value1 - value2;
            } else if (operation[0] == 'm') {
                result = value1 * value2;
            } else {
                result = value1 / value2;
            }
            sprintf(operationCommand, "%s %d %d", operation, value1, value2);
        }
        printf("[Server] Info: send \"%s\" to client\n", operationCommand);

        if (send(client_fd, operationCommand, MAXRECVSIZE, 0) == -1) {      //发送计算题给客户端
            printf("[Server] Error: send error\n");
            shutdown(client_fd, SHUT_RDWR);
            close(client_fd);
            continue; //leave loop execution, go back to the while, main accept() loop.
        }

        //阻塞接收客户端的消息
        memset(msg, 0, MAXRECVSIZE);    //重新初始化接受客户端的消息容器，来重复使用
        if ((readsize = recv(client_fd, &msg, MAXRECVSIZE, 0)) == -1) {
            printf("[Server] Error: Receive client answer timeout, send ERROR to client.\n");
            send(client_fd, "ERROR", sizeof("ERROR"), 0);
            break;
        }
        char resultstr[MAXRECVSIZE];   //存储服务端的计算结果

        printf("[Server] Info: client -> my answer to \"%s\" is: %s\n", operationCommand, msg);
        //将之前的计算数值转化为字符串
        if (operation[0] == 'f') {
            sprintf(resultstr, "%8.8g", fresult);
        } else {
            sprintf(resultstr, "%d", result);
        }

        printf("[Server] Info: the right answer should be %s\n", resultstr);
        send(client_fd, resultstr, sizeof(resultstr), 0);   //将服务端的计算结果返回给客户端

        //比较服务端和客户端的计算结果并根据此返回OK还是ERROR给客户端
        if (strcmp(msg, resultstr) == 0) {
            printf("[Server] Info: right answer\n");
            bzero(resultstr, sizeof(resultstr));  //初始化返回数组，重新利用
            sprintf(resultstr, "OK");
        } else {
            printf("[Server] Info: wrong answer\n");
            bzero(resultstr, sizeof(resultstr));  //初始化返回数组，重新利用
            sprintf(resultstr, "ERROR");
        }
        send(client_fd, resultstr, sizeof(resultstr), 0);

        sleep(1);
        close(client_fd);
        printf("[Server] Info: mission has done. stop connection.\n");
        break;
    }
}

int sendproto() {
    printf("[Server] Info: Sending support communication protocols \n");
    if (send(client_fd, "TEXT TCP 1.0", 13, 0) == -1) {   //发送服务端支持的协议
        printf("[Server] Error: send error\n");
        shutdown(client_fd, SHUT_RDWR);
        close(client_fd);
        return -1;   //当发送不了时，放弃当前客户端并等待下一个客户端
    }
    return 0;
}

int main(int argc, char *argv[]) {
    const char *delim = ":";
    const char *server_ip = strtok(argv[1], delim);
    int server_port = atoi(strtok(NULL, delim));
    if (argc > 2) {
        if_sleep = true;
    }
    initsocket(server_ip, server_port);
    if (if_sleep) {
        sleep(15);
    }
    //一次只能与一个客户端对接，可以同步阻塞式处理不同客户端
    while (1) {
        printf("-------------client[%d]-------------\n", clientid);
        if ((client_fd = accept(listen_fd, (struct sockaddr *) &client, &addrlen)) == -1) {    //监听接收来自客户端的连接请求
            printf("[Server] Error: accept connection error\n");
            continue;
        }
        setsockopt(client_fd, SOL_SOCKET, SO_RCVTIMEO, (char *) &timeout, sizeof(timeout));
        printf("[Server] Info: establish a connection from client %s:%d\n", inet_ntoa(client.sin_addr),
               htons(client.sin_port));

        //协商协议
        if (sendproto() != 0) {
            continue;
        }

        handleconnection();

        //将所处理过的客户端数量加一，以计数
        clientid++;
    }

    close(listen_fd);
    return 0;
}